package com.qf.shiro2203.config;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.shiro2203.userr.dao.*;
import com.qf.shiro2203.userr.entity.*;
import com.qf.shiro2203.userr.service.*;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CustomRealm extends AuthorizingRealm {
    @Autowired
    UserDao userDao;
    @Autowired
    PersDao persDao;
    @Autowired
    RoleDao roleDao;
    @Autowired
    UserRoleDao userRoleDao;
    @Autowired
    RolePermsDao rolePermsDao;
    //------------------------------
    @Autowired
    IUserService userService;
    @Autowired
    IPersService persService;
    @Autowired
    IRolePermsService rolePermsService;
    @Autowired
    IRoleService roleService;
    @Autowired
    IUserRoleService userRoleService;
    /**
     * 通过这个方法，向shiro提供 当前用户的身份认证信息
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获得用户名
        String username = (String) token.getPrincipal();
        //根据 token中的用户提供的身份信息（用户名），去数据库中查询用户密码
        User user= getUserFromDB(username);
        //如果username页面那边写错了，那样user会为null
        //user为null 那么接下来就会空指针，有异常，mvc会吧当前请求forward到 /error url中
        if(user==null){
            throw new UnknownAccountException("用户名不存在");

        }
        // 需要把数据库中的用户的密码返回给shiro
        // 注意，第一个参数，可以放入从数据库中查询出来的完整的user对象，也可以只放入用户名。这里会影响后续从shiro中获取用户身份信息时获取到的对象
        // 如果放入user对象，后续获取的身份信息就是user对象
        // 第二个参数，放入数据库中查询出来的用户的密码
        // 第三个参数，是当前提供身份信息的Realm对象的名字  固定写法 this.getName()
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),/*把当前用户的盐（salt）给shiro*/
                this.getName());


        return simpleAuthenticationInfo;
    }

    /**
     * 查询数据库，获得user对象
     * @param username
     */
    private User getUserFromDB(String username) {
        //userService方法
       // User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getName, username));
        //userDao方法
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("name",username);
        User user = userService.getOne(userQueryWrapper);
        // User user = userDao.selectOne(userQueryWrapper);
//------------------------------------------------------------------------------------------------------
        //1.查询userRole 条件是登录用户名的id  想查询到roleid
        LambdaQueryWrapper<UserRole> userRoleLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userRoleLambdaQueryWrapper.select(UserRole::getRoleid);
        userRoleLambdaQueryWrapper.eq(UserRole::getUserid,user.getId());
        List<Object> roleIdList = userRoleService.listObjs(userRoleLambdaQueryWrapper);

        //2.查询role_perms 条件是上一步查询出来的 角色id  想查询到permsid
        QueryWrapper<RolePerms> rolePermsQueryWrapper = new QueryWrapper<>();
        rolePermsQueryWrapper.select("permsid")
                .in("roleid",roleIdList);
        List<Object> permsIdList = rolePermsService.listObjs(rolePermsQueryWrapper);

        //3.查询 pers,条件是上一步的permsIdList  想查询到name
        QueryWrapper<Pers> persQueryWrapper = new QueryWrapper<>();
        persQueryWrapper.select("name")
                .in("id",permsIdList);
        List<Object> persNameList = persService.listObjs(persQueryWrapper);
        //4.list<object>-----> list<string>   普通版
        ArrayList<String> persNameStringList = new ArrayList<>();

        for (Object o : persNameList) {
            persNameStringList.add(o.toString());
        }
        //--------------------------------------------------------------------
//知道了当前用户的id，就在UserRole表中 因为有用户的id 查询到roleid

//最后在Role表中，用拿到的roleid 去查询name （因为可能是多个 所以要用in()链接符连接）
        QueryWrapper<Role> roleQueryWrapper = new QueryWrapper<>();
        roleQueryWrapper.in("id", roleIdList);
        roleQueryWrapper.select("name");
        List<Object> roleNameList = roleService.listObjs(roleQueryWrapper);
        //List<Object> roleNameList = roleDao.selectObjs(roleQueryWrapper);

        //转换集合中的类型  list<object> -----> list<string>   升级版 （尽量掌握）
        List<String> roleNameStringList = roleNameList.stream()//转出stream流对象
                .map(obj -> obj.toString())   //用lamda的方式  把原集合中的每一个元素转换成string
                .collect(Collectors.toList()); //把stream对象在转换成List
        user.setPerms(persNameStringList);
        user.setRoles(roleNameStringList);

        return user;
    }

    /**
     * 通过这个方法，向Shiro提供当前用户的 权限相关信息
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        //获取当前用户的身份信息
        User user = (User) principals.getPrimaryPrincipal();
        //去数据库中查询user对应的 角色和权限信息
      /*List<String> roles= getRolesFromDB(user);
        List<String> perms= getPermsFromDB(user);*/

        //把角色和权限信息封装到 AuthorizationInfo 对象中
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //添加角色信息
        //simpleAuthorizationInfo.addRole("admin");
        /*simpleAuthorizationInfo.addRoles(Arrays.asList("admin","normal"));*/
        simpleAuthorizationInfo.addRoles(user.getRoles()); //为了从数据库中获得角色信息，不是写死的
        //添加权限信息
        //simpleAuthorizationInfo.addStringPermission("order:get");
        simpleAuthorizationInfo.addStringPermissions(user.getPerms());  //为了从数据库中获得权限信息，不是写死的
        return simpleAuthorizationInfo;
    }

    /**
     * 查询权限
     * @param user
     * @return
     */
    /*private List<String> getPermsFromDB(User user) {
        //1.查询userRole 条件是登录用户名的id  想查询到roleid
        LambdaQueryWrapper<UserRole> userRoleLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userRoleLambdaQueryWrapper.select(UserRole::getRoleid);
        userRoleLambdaQueryWrapper.eq(UserRole::getUserid,user.getId());
        List<Object> roleIdList = userRoleService.listObjs(userRoleLambdaQueryWrapper);

        //2.查询role_perms 条件是上一步查询出来的 角色id  想查询到permsid
        QueryWrapper<RolePerms> rolePermsQueryWrapper = new QueryWrapper<>();
        rolePermsQueryWrapper.select("permsid")
                .in("roleid",roleIdList);
        List<Object> permsIdList = rolePermsService.listObjs(rolePermsQueryWrapper);

        //3.查询 pers,条件是上一步的permsIdList  想查询到name
        QueryWrapper<Pers> persQueryWrapper = new QueryWrapper<>();
        persQueryWrapper.select("name")
                .in("id",permsIdList);
        List<Object> persNameList = persService.listObjs(persQueryWrapper);
        //4.list<object>-----> list<string>   普通版
        ArrayList<String> persNameStringList = new ArrayList<>();

        for (Object o : persNameList) {
            persNameStringList.add(o.toString());
        }

        return persNameStringList;
    }*/

    /**
     * 查询身份
     * @param user
     * @return
     */
/*    private List<String> getRolesFromDB(User user) {
//有写多余  但是清楚明了   想看看登录的用户，因为知道name 查询到id是多少
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("name",user.getName());
        userQueryWrapper.select("id");
        User user1 = userDao.selectOne(userQueryWrapper);
//知道了当前用户的id，就在UserRole表中 因为有用户的id 查询到roleid
        QueryWrapper<UserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper.eq("userid",user1.getId());
        userRoleQueryWrapper.select("roleid");
        List<Object> roleIdList = userRoleDao.selectObjs(userRoleQueryWrapper);
//最后在Role表中，用拿到的roleid 去查询name （因为可能是多个 所以要用in()链接符连接）
            QueryWrapper<Role> roleQueryWrapper = new QueryWrapper<>();
            roleQueryWrapper.in("id", roleIdList);
            roleQueryWrapper.select("name");
        List<Object> roleNameList = roleDao.selectObjs(roleQueryWrapper);

        //转换集合中的类型  list<object> -----> list<string>   升级版 （尽量掌握）
        List<String> roleNameStringList = roleNameList.stream()//转出stream流对象
                .map(obj -> obj.toString())   //用lamda的方式  把原集合中的每一个元素转换成string
                .collect(Collectors.toList()); //把stream对象在转换成List

        return roleNameStringList;
    }*/
}
